{
 "cells": [
  {
   "cell_type": "markdown",
   "source": [
    "# 面向对象基础"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%% md\n"
    }
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "## 定义类\n",
    "在Python中可以使用class关键字定义类，然后在类中通过函数来定义方法，这样就可以将对象的动态特征描述出来，代码如下所示。"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%% md\n"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "outputs": [],
   "source": [
    "class Student(object):\n",
    "    # __init__是一个特殊方法用于在创建对象时进行初始化操作\n",
    "    # 通过这个方法我们可以为学生对象绑定name和age两个属性\n",
    "    def __init__(self, name, age):\n",
    "        self.name = name\n",
    "        self.age = age\n",
    "\n",
    "    def study(self, course_name):\n",
    "        print(\"%s正在学习%s\"% (self.name, course_name))\n",
    "\n",
    "    def watch_movie(self):\n",
    "        if  self.age < 18:\n",
    "            print(\"%s只能观看<熊出没>\"% self.name)\n",
    "        else:\n",
    "            print(\"%s正在观看FBI大电影.\"% self.name)"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "说明： 写在类中的函数，我们通常称之为（对象的）方法，这些方法就是对象可以接收的消息。"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%% md\n"
    }
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "## 创建和使用对象\n",
    "当我们定义好一个类之后，可以通过下面的方式来创建对象并调用方法"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%% md\n"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "yzpang正在学习离散数学\n",
      "yzpang只能观看<熊出没>\n"
     ]
    }
   ],
   "source": [
    "def main():\n",
    "    # 创建学生对象并指定姓名与年龄\n",
    "    stu1 = Student('yzpang', 12)\n",
    "    # 调用study方法\n",
    "    stu1.study('离散数学')\n",
    "    # 调用watch_movie方法\n",
    "    stu1.watch_movie()\n",
    "\n",
    "main()"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "## 访问可见性问题\n",
    "我们给Student对象绑定的name和age属性到底具有怎样的访问权限（也称为可见性）?\n",
    "在Python中，属性和方法的访问权限只有两种，也就是公开的和私有的，\n",
    "如果希望属性是私有的，在给属性命名时可以用两个下划线作为开头\n",
    "private:    __name\n",
    "protected:  _name\n",
    "public:     name\n",
    "在类体中，根据变量定义的位置不同，以及定义的方式不同，类属性又可细分为以下 3 种类型：\n",
    "1.类体中、所有函数之外：此范围定义的变量，称为类属性或类变量；\n",
    "2.类体中，所有函数内部：以“self.变量名”的方式定义的变量，称为实例属性或实例变量；\n",
    "3.类体中，所有函数内部：以“变量名=变量值”的方式定义的变量，称为局部变量。"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%% md\n"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "outputs": [
    {
     "ename": "AttributeError",
     "evalue": "'Test' object has no attribute '__bar'",
     "output_type": "error",
     "traceback": [
      "\u001B[1;31m---------------------------------------------------------------------------\u001B[0m",
      "\u001B[1;31mAttributeError\u001B[0m                            Traceback (most recent call last)",
      "Cell \u001B[1;32mIn[13], line 14\u001B[0m\n\u001B[0;32m     11\u001B[0m     test\u001B[38;5;241m.\u001B[39m__bar()\n\u001B[0;32m     12\u001B[0m     \u001B[38;5;28mprint\u001B[39m(test\u001B[38;5;241m.\u001B[39m__foo)\n\u001B[1;32m---> 14\u001B[0m \u001B[43mmain\u001B[49m\u001B[43m(\u001B[49m\u001B[43m)\u001B[49m\n",
      "Cell \u001B[1;32mIn[13], line 11\u001B[0m, in \u001B[0;36mmain\u001B[1;34m()\u001B[0m\n\u001B[0;32m      9\u001B[0m \u001B[38;5;28;01mdef\u001B[39;00m \u001B[38;5;21mmain\u001B[39m():\n\u001B[0;32m     10\u001B[0m     test \u001B[38;5;241m=\u001B[39m Test(\u001B[38;5;124m'\u001B[39m\u001B[38;5;124mhello\u001B[39m\u001B[38;5;124m'\u001B[39m)\n\u001B[1;32m---> 11\u001B[0m     \u001B[43mtest\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43m__bar\u001B[49m()\n\u001B[0;32m     12\u001B[0m     \u001B[38;5;28mprint\u001B[39m(test\u001B[38;5;241m.\u001B[39m__foo)\n",
      "\u001B[1;31mAttributeError\u001B[0m: 'Test' object has no attribute '__bar'"
     ]
    }
   ],
   "source": [
    "class Test:\n",
    "    def __init__(self, foo):\n",
    "        self.__foo = foo\n",
    "\n",
    "    def __bar(self):\n",
    "        print(self.__foo)\n",
    "        print('__bar')\n",
    "\n",
    "def main():\n",
    "    test = Test('hello')\n",
    "    test.__bar()\n",
    "    print(test.__foo)\n",
    "\n",
    "main()"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "但是，Python并没有从语法上严格保证私有属性或方法的私密性，它只是给私有的属性和方法换了一个名字来妨碍对它们的访问，\n",
    "事实上如果你知道更换名字的规则仍然可以访问到它们，下面的代码就可以验证这一点。"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%% md\n"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "hello\n",
      "__bar\n",
      "hello\n"
     ]
    }
   ],
   "source": [
    "def main1():\n",
    "    test = Test('hello')\n",
    "    test._Test__bar()\n",
    "    print(test._Test__foo)\n",
    "\n",
    "main1()"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "在实际开发中，我们并不建议将属性设置为私有的，因为这会导致子类无法访问（后面会讲到）。所以大多数Python程序员会遵循一种命名惯例就是让属性名以单下划线开头来表示属性是受保护的，本类之外的代码在访问这样的属性时应该要保持慎重。"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%% md\n"
    }
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "示例1: 描述一个时钟"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%% md\n"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "16:53:45\n",
      "16:53:46\n",
      "16:53:47\n",
      "16:53:48\n",
      "16:53:49\n",
      "16:53:50\n",
      "16:53:51\n",
      "16:53:52\n",
      "16:53:53\n",
      "16:53:54\n",
      "16:53:55\n",
      "16:53:56\n"
     ]
    },
    {
     "ename": "KeyboardInterrupt",
     "evalue": "",
     "output_type": "error",
     "traceback": [
      "\u001B[1;31m---------------------------------------------------------------------------\u001B[0m",
      "\u001B[1;31mKeyboardInterrupt\u001B[0m                         Traceback (most recent call last)",
      "Cell \u001B[1;32mIn[18], line 38\u001B[0m\n\u001B[0;32m     35\u001B[0m         sleep(\u001B[38;5;241m1\u001B[39m)\n\u001B[0;32m     36\u001B[0m         clock\u001B[38;5;241m.\u001B[39mrun()\n\u001B[1;32m---> 38\u001B[0m \u001B[43mmain\u001B[49m\u001B[43m(\u001B[49m\u001B[43m)\u001B[49m\n",
      "Cell \u001B[1;32mIn[18], line 35\u001B[0m, in \u001B[0;36mmain\u001B[1;34m()\u001B[0m\n\u001B[0;32m     33\u001B[0m \u001B[38;5;28;01mwhile\u001B[39;00m \u001B[38;5;28;01mTrue\u001B[39;00m:\n\u001B[0;32m     34\u001B[0m     \u001B[38;5;28mprint\u001B[39m(clock\u001B[38;5;241m.\u001B[39mshow())\n\u001B[1;32m---> 35\u001B[0m     \u001B[43msleep\u001B[49m\u001B[43m(\u001B[49m\u001B[38;5;241;43m1\u001B[39;49m\u001B[43m)\u001B[49m\n\u001B[0;32m     36\u001B[0m     clock\u001B[38;5;241m.\u001B[39mrun()\n",
      "\u001B[1;31mKeyboardInterrupt\u001B[0m: "
     ]
    }
   ],
   "source": [
    "from time import sleep\n",
    "\n",
    "\n",
    "class Clock(object):\n",
    "    \"\"\"数字时钟\"\"\"\n",
    "\n",
    "    def __init__(self, hour=0, minute=0, second=0):\n",
    "        \"\"\"构造方法\"\"\"\n",
    "        self._hour = hour\n",
    "        self._minute = minute\n",
    "        self._second = second\n",
    "\n",
    "    def run(self):\n",
    "        \"\"\"时钟走一秒\"\"\"\n",
    "        self._second += 1\n",
    "        if  self._second == 60:\n",
    "            self._second = 0\n",
    "            self._minute += 1\n",
    "            if self._minute == 60:\n",
    "                self._minute = 0\n",
    "                self._hour += 1\n",
    "                if self._hour == 24:\n",
    "                    self._hour = 0\n",
    "        pass\n",
    "\n",
    "    def show(self):\n",
    "        \"\"\"显示时间\"\"\"\n",
    "        return '%02d:%02d:%02d' % (self._hour, self._minute, self._second)\n",
    "\n",
    "\n",
    "def main():\n",
    "    clock = Clock(16, 53, 45)\n",
    "    while True:\n",
    "        print(clock.show())\n",
    "        sleep(1)\n",
    "        clock.run()\n",
    "\n",
    "main()"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "示例2: 描述平面上的一个点"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%% md\n"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(3, 5)\n",
      "(0, 0)\n",
      "(2, 7)\n",
      "(1, 1)\n",
      "6.082762530298219\n"
     ]
    }
   ],
   "source": [
    "from math import sqrt\n",
    "\n",
    "\n",
    "class Point(object):\n",
    "    \"\"\"平面点\"\"\"\n",
    "    def __init__(self, x=0, y=0):\n",
    "        \"\"\"构造方法\"\"\"\n",
    "        self.x = x\n",
    "        self.y = y\n",
    "\n",
    "    def move_to(self, x, y):\n",
    "        \"\"\"移动到指定位置\"\"\"\n",
    "        self.x = x\n",
    "        self.y = y\n",
    "\n",
    "    def move_by(self, dx, dy):\n",
    "        \"\"\"移动指定的偏移\"\"\"\n",
    "        self.x += dx\n",
    "        self.y += dy\n",
    "\n",
    "    def distance_to(self, other):\n",
    "        \"\"\"与另一个点的距离\"\"\"\n",
    "        dx = self.x - other.x\n",
    "        dy = self.y - other.y\n",
    "        return sqrt(dx**2 + dy**2)\n",
    "\n",
    "    def __str__(self):\n",
    "        \"\"\"toString()\"\"\"\n",
    "        return '(%s, %s)' % (str(self.x), str(self.y))\n",
    "\n",
    "def main():\n",
    "    p1 = Point(3, 5)\n",
    "    p2 = Point()\n",
    "    print(p1)\n",
    "    print(p2)\n",
    "    p1.move_by(-1, 2)\n",
    "    p2.move_to(1, 1)\n",
    "    print(p1)\n",
    "    print(p2)\n",
    "    print(p1.distance_to(p2))\n",
    "\n",
    "main()"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}